package com.laght.jdkdemo.jdk21demo;

import com.laght.jdkdemo.jdk21demo.patternmatchingforswitch.PatternMatchingForSwitchADTO;
import com.laght.jdkdemo.jdk21demo.patternmatchingforswitch.PatternMatchingForSwitchBDTO;
import com.laght.jdkdemo.jdk21demo.recordPatterns.RecordPatternsDTO;
import com.laght.jdkdemo.jdk21demo.scopedvalue.ScopedValueDTO;
import com.laght.jdkdemo.jdk21demo.scopedvalue.ScopedValueService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.text.MessageFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * java 21 新特性演示案例
 * Oracle Java官方文档：https://www.oracle.com/java/technologies/javase/21-relnote-issues.html
 * Idea下载链接：https://www.jetbrains.com.cn/idea/download/?section=windows
 * jdk21特性官方文档：https://openjdk.org/projects/jdk/21/
 *
 * 1. 语言功能
 * Record Patterns 记录模式
 * 使用记录模式增强Java编程语言，以解构记录值。可以嵌套记录模式和类型模式，以支持强大的、声明性的和可组合的数据导航和处理形式。
 * 参见JEP 440
 *
 * Pattern Matching for switch 开关的模式匹配
 * 使用switch表达式和语句的模式匹配增强Java编程语言。将模式匹配扩展到switch允许针对多个模式对表达式进行测试，每个模式都有一个特定的操作，这样就可以简明而安全地表达复杂的面向数据的查询。
 * 参见JEP 441
 *
 *
 * 1.1语言特性预览
 * String Templates(字符串模板)(预览)
 * 使用字符串模板增强Java编程语言。字符串模板通过将文本与嵌入表达式和模板处理器耦合来产生专门的结果，从而补充了Java现有的字符串文字和文本块。这是一个预览语言特性和API。
 * 参见JEP 430
 *
 * Unnamed Patterns and Variables（未命名模式和变量）(预览)
 * 使用未命名模式增强Java语言，未命名模式匹配记录组件而不声明组件的名称或类型，以及未命名变量，这些变量可以初始化但不能使用。两者都由下划线_表示。这是一个预览语言特性。
 * 参见JEP 443
 *
 * Unnamed Classes and Instance Main Methods（未命名类和实例主方法）(预览)
 * 发展Java语言，使学生可以编写他们的第一个程序，而不需要了解为大型程序设计的语言特性。学生不必使用单独的Java方言，而是可以为单类程序编写精简的声明，然后随着技能的提高，无缝地扩展程序，使用更高级的功能。这是一个预览语言特性。
 * 参见JEP 445
 *
 * 2. 图书馆的改进
 * Virtual Threads 虚拟线程
 * 向Java平台引入虚拟线程。虚拟线程是轻量级线程，可以显著减少编写、维护和观察高吞吐量并发应用程序的工作量。
 * 参见JEP 444
 *
 * Sequenced Collections 序列集合
 * 引入新的接口来表示具有定义的相遇顺序的集合。每个这样的集合都有定义良好的第一个元素、第二个元素，依此类推，直到最后一个元素。它还提供了统一的api来访问它的第一个和最后一个元素，并以相反的顺序处理它的元素。
 * 参见JEP 431
 *
 * Key Encapsulation Mechanism API 密钥封装机制API
 * 引入密钥封装机制(kem)的API，这是一种使用公钥加密保护对称密钥的加密技术。
 * 参见JEP 452
 *
 *
 * 2.1库改进预览和孵化器
 * Foreign Function & Memory API 外部函数和内存API(第三次预览)
 * 引入一个API，通过该API, Java程序可以与Java运行时之外的代码和数据进行互操作。通过有效地调用外部函数(即JVM外部的代码)和安全地访问外部内存(即JVM不管理的内存)，API使Java程序能够调用本机库并处理本机数据，而不会出现JNI的脆弱性和危险。这是一个预览API。
 * 参见JEP 442
 *
 * Structured Concurrency (Preview) 结构化并发(预览)
 * 通过引入结构化并发的API来简化并发编程。结构化并发将在不同线程中运行的相关任务组视为单个工作单元，从而简化了错误处理和取消，提高了可靠性，并增强了可观察性。这是一个预览API。
 * 参见JEP 453
 *
 * Scoped Values 作用域值 (预览)
 * 引入有作用域的值，这些值可以在不使用方法参数的情况下安全有效地共享给方法。它们比线程局部变量更可取，特别是在使用大量虚拟线程时。这是一个预览API。
 * 实际上，作用域值是隐式方法参数。这就“好像”调用序列中的每个方法都有一个额外的、不可见的参数。没有任何方法声明这个参数，只有能够访问作用域值对象的方法才能访问它的值(数据)。有作用域的值可以通过一系列中间方法将数据从调用者安全地传递给远程被调用者，这些中间方法不为数据声明参数，也不能访问数据。
 * 参见JEP 446
 *
 * Vector API(第六个孵化器)
 * 引入API来表达矢量计算，在运行时可靠地在支持的CPU架构上编译为最优矢量指令，从而实现优于等效标量计算的性能。
 * 参见JEP 448
 *
 *
 * 3. 性能改进
 * Generational ZGC 分代ZGC
 * 通过扩展Z垃圾收集器(ZGC)来为年轻对象和老对象维护单独的代，从而提高应用程序性能。这将允许ZGC更频繁地收集年轻的对象——它们往往会在年轻时死亡。
 * 参见JEP 439
 *
 * 4. 管理
 * Prepare to Disallow the Dynamic Loading of Agents 准备禁止动态加载代理
 * 在将代理动态加载到正在运行的JVM中时发出警告。这些警告的目的是让用户为将来的版本做好准备，该版本默认情况下不允许动态加载代理，以便在默认情况下提高完整性。在启动时加载代理的可服务性工具不会在任何版本中发出警告。
 * 参见JEP 451
 */
@SpringBootTest
class Jdk21Demo {

    /**
     * Record Patterns（记录模式）：允许开发人员使用模式来解构记录类的实例，从而简化数据查询。
     * 优点：记录类的定义非常简洁，只需要列出其属性和可能的构造方法
     *      Java编译器会自动生成默认构造器\getter\equals(), hashCode() 和 toString() 方法\copy() 方法（如果记录类有可变字段）用于创建具有部分或全部字段更改的新记录实例
     *      两个记录实例被认为是相等的，当且仅当它们的类型相同并且所有属性值都相等。
     *      可以方便地在switch表达式和其他上下文中解构记录实例。
     * 缺点：记录类是不可变的，即创建后其属性值不能改变。记录类不能被继承，也不能实现接口（除非是函数式接口，并提供一个抽象方法的实现）。
     * 适用场景：主要用于表示简单的数据结构，强调数据的不可变性和简洁性。
     */
    @Test
    void recordPatternsTest(){
        Object recordPatternsDTO = new RecordPatternsDTO("Alice", 30);

        switch (recordPatternsDTO) {
            case RecordPatternsDTO record -> {
                if (record.age() > 30) {
                    System.out.println(record.name() + " she is " + record.age() + " years old.");
                }else{
                    System.out.println(record.name() + " he is " + record.age() + " years old.");
                }
            }
            default -> {
                System.out.println("记录类型不符");
            }
        }
    }

    /**
     * String Templates（字符串模板）：允许开发人员在 Java 代码中使用模板来生成字符串，从而简化字符串的编写和维护。
     * 优点：
     * 缺点：
     * 适用场景：
     */
    @Test
    void stringTemplatesTest(){
        String name = "John Doe";
        int age = 30;

        // 使用MessageFormat类创建字符串模板
        String template = "Hello, {0}. You are {1} years old.";

        // 使用format()方法替换模板中的占位符
        String messageFormattedString = MessageFormat.format(template, name, age);

        System.out.println(messageFormattedString);  // 输出：Hello, John Doe. You are 30 years old.



        // 使用String.format()方法创建字符串模板
        String stringFormattedString = String.format("Hello, %s. You are %d years old.", name, age);

        System.out.println(stringFormattedString);  // 输出：Hello, John Doe. You are 30 years old.
    }

    /**
     * Pattern Matching for switch（switch 中的模式匹配）：允许开发人员在 switch 语句中使用模式匹配，从而简化复杂的数据查询。
     * 优点：省去了确定实例类型之后显示强转的步骤
     * 缺点：
     * 适用场景：
     */
    @Test
    void patternMatchingForSwitchTest(){
//        Object obj = null;
//        Object obj = "字符串";
//        Object obj = 1;
//        Object obj = new PatternMatchingForSwitchADTO("PatternMatchingForSwitchADTO");
        Object obj = new PatternMatchingForSwitchBDTO("PatternMatchingForSwitchBDTO");
        switch (obj) {
            case String s -> {
                // 处理字符串
                s = "-------" + s + "-------";
                System.out.println("字符串处理：" + s);
            }
            case Integer i -> {
                // 处理整数
                i++;
                System.out.println("整数处理：" + i);
            }
            case null -> {
                // 对null的处理
                System.out.println("null处理");
            }
            case PatternMatchingForSwitchADTO patternMatchingForSwitchADTO -> {
                // 直接使用patternMatchingForSwitchADTO变量，无需显式类型转换
                // 对null的处理
                System.out.println("PatternMatchingForSwitchADTO处理");
            }
            default -> {
                // 其他类型的处理
                if (obj instanceof PatternMatchingForSwitchBDTO patternMatchingForSwitchBDTO) {
                    // 直接使用str变量，无需显式类型转换
                    System.out.println("patternMatchingForSwitchBDTO处理");
                }
            }
        }


    }

    /**
     * JEP 443：Unnamed Patterns and Variables（匿名模式和变量）：允许开发人员使用匿名模式和变量，从而简化代码的编写和维护。
     * 优点：
     * 缺点：
     * 适用场景：
     */
    @Test
    void unnamedPatternsVariablesTest(){

    }

    /**
     * JEP 445：Unnamed Classes and Instance Main Methods（匿名类和实例主方法）：允许开发人员使用匿名类和实例主方法来简化简单程序的编写。
     * 优点：
     * 缺点：
     * 适用场景：
     */
    @Test
    void unnamedClassesInstanceMainMethodsTest(){

    }

    /**
     * Virtual Threads 虚拟线程
     * 向Java平台引入虚拟线程。虚拟线程是轻量级线程，可以显著减少编写、维护和观察高吞吐量并发应用程序的工作量。
     * 因为虚拟线程是廉价的，平台线程是昂贵的，所以不要为了限制并发性而试图将虚拟线程池化。而是使用专门为此目的设计的结构，例如信号量。
     * 结合线程池，开发人员有时使用线程局部变量在共享同一线程的多个任务之间共享昂贵的资源。例如，如果创建数据库连接的成本很高，那么您可以打开它一次，并将其存储在线程本地变量中，以供同一线程中的其他任务稍后使用。如果将代码从使用线程池迁移到每个任务使用一个虚拟线程，那么要小心使用这种习惯用法，因为为每个虚拟线程创建昂贵的资源可能会显著降低性能。更改这些代码以使用替代缓存策略，以便在大量虚拟线程之间有效地共享昂贵的资源。
     * 优点：可以最大化的利用平台线程的资源，不需要和平台线程一一对应，可以N:1的模式，同时处理大量线程,由 JVM直接进行管理和调度的，因此具有很低的创建和销毁开销。它们更轻量级，适用于大规模的并发任务。
     * 缺点：由操作系统进行管理和调度，创建和销毁线程可能涉及到更大的开销。内存开销相对较大，受到系统资源的限制。传统的平台线程可能需要更多的同步和锁机制，以确保正确的线程协同。
     * 适用场景：
     */
    @Test
    void virtualThreadsTest() throws ExecutionException, InterruptedException {
        //虚拟线程
        //Thread.ofVirtual().start(()->{})
        executeVirtualThread1();
        //CompletableFuture.runAsync(() -> {})
        executeVirtualThread2();
        //Executors.newVirtualThreadPerTaskExecutor().submit(() -> {})
        executeVirtualThread3();
    }


    @Test
    void compareThread() {
        testVirtualThreads();
        testPlatformThreads();

        /**
         * 1000个线程，每个线程循环10000次自增数据
         * === Testing Virtual Threads ===
         * s=1000000001
         * ExecutorService耗时3PT16.449556S
         * === Testing Platform Threads ===
         * s=1000000001
         * ExecutorService耗时5PT13.6450819S
         *
         * === Testing Virtual Threads ===
         * s=1000000001
         * ExecutorService耗时3PT17.246364S
         * === Testing Platform Threads ===
         * s=1000000001
         * ExecutorService耗时5PT15.7024936S
         *
         * 10000个线程，每个线程循环10000次自增数据
         * === Testing Virtual Threads ===
         * s=1410065409
         * ExecutorService耗时3PT2M43.4721031S
         * === Testing Platform Threads ===
         * s=1410065409
         * ExecutorService耗时5PT2M18.9186834S
         *
         * 1000000个线程，每个线程循环10000次自增数据
         * === Testing Virtual Threads ===
         * s=1410065409
         * ExecutorService耗时3PT2M35.6649194S
         * === Testing Platform Threads ===
         * 等待半小时后，电脑卡死，执行失x败
         *
         * 总结，虚拟线程与平台线程相比，性能相当，平台线程略快一点，
         * 并发量上，虚拟线程不会因为并发量过大而导致执行失败，平台线程会出现异常，
         * 效果上，虚拟线程对资源的利用度高，平台线程比较低
         */
    }

    private static void testVirtualThreads() {
        System.out.println("=== Testing Virtual Threads ===");
        LocalDateTime start3 = LocalDateTime.now();
        List<Thread> virtualThreads = new ArrayList<>();

        AtomicInteger s = new AtomicInteger(1);
        // 创建大量的虚拟线程
        for (int i = 0; i < 1000000; i++) {
            int finalI = i;
            Thread virtualThread = Thread.ofVirtual().start(() -> {
                // 执行任务
                for (int j = 0; j < 10000; j++) {
                    // 模拟任务逻辑
                    s.getAndIncrement();
                }
            });
            virtualThreads.add(virtualThread);
        }

        // 等待虚拟线程完成
        virtualThreads.forEach(thread -> {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        LocalDateTime end3 = LocalDateTime.now();
        Duration between3 = Duration.between(start3, end3);
        System.out.println("s="+s);
        System.out.println("ExecutorService耗时3"+between3);
    }

    private static void testPlatformThreads() {
        System.out.println("=== Testing Platform Threads ===");
        LocalDateTime start5 = LocalDateTime.now();
        List<Thread> platformThreads = new ArrayList<>();

        AtomicInteger s = new AtomicInteger(1);
        // 创建大量的平台线程
        for (int i = 0; i < 1000000; i++) {
            int finalI = i;
            Thread platformThread = new Thread(() -> {
                // 执行任务
                for (int j = 0; j < 10000; j++) {
                    // 模拟任务逻辑
                    s.getAndIncrement();
                }
            });
            platformThread.start();
            platformThreads.add(platformThread);
        }

        // 等待平台线程完成
        platformThreads.forEach(thread -> {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        LocalDateTime end5 = LocalDateTime.now();
        Duration between5 = Duration.between(start5, end5);
        System.out.println("s="+s);
        System.out.println("ExecutorService耗时5"+between5);
    }

    /**
     * 优点：
     * 提供了一个虚拟线程池，适用于管理大量的虚拟线程。
     * 允许更灵活的线程池配置。
     * 缺点：
     * 相对于其他方式来说，可能稍微繁琐。
     * 对于简单的线程创建，可能有些过于重量级。
     */
    private void executeVirtualThread3() {
        ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
        LocalDateTime start3 = LocalDateTime.now();
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            executor.submit(() -> {
                // 虚拟线程的执行逻辑
                for (int j = 0; j < 10000; j++) {
                    // 模拟任务逻辑
                    System.out.println(finalI+"-"+j);
                }
            });
        }

        // 关闭执行器
        executor.shutdown();

        try {
            // 等待所有任务完成
            if (executor.awaitTermination(1000, TimeUnit.SECONDS)) {
                LocalDateTime end3 = LocalDateTime.now();
                Duration between3 = Duration.between(start3, end3);
                System.out.println("ExecutorService耗时3"+between3);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 优点：
     * 使用CompletableFuture可以方便地处理异步任务。
     * 可以利用join()等方法等待线程完成。
     * 缺点：
     * 对于一些更高级的线程控制，可能不够灵活。
     * 如果需要更复杂的任务组合，可能需要额外的操作。
     * 适用场景：
     *     并发任务： 当你有多个独立的任务需要并行执行时，CompletableFuture 可以帮助你更方便地管理和组合这些任务的结果。它允许你以异步的方式执行这些任务，而不需要手动管理线程池和线程。
     *     异步操作： 当你需要执行异步操作，并在操作完成后执行一些逻辑时，CompletableFuture 提供了丰富的方法来处理异步任务的结果。
     *     任务组合： 如果你有一组相互独立的任务，而且这些任务之间的执行顺序没有特定要求，CompletableFuture 的 allOf() 和 anyOf() 方法可以帮助你在所有任务完成或任何一个任务完成时触发相应的逻辑。
     *     依赖关系： 当某个任务依赖于其他任务的结果时，CompletableFuture 允许你使用 thenApply(), thenAccept(), thenCompose() 等方法来定义这些任务之间的依赖关系。
     *     异常处理： CompletableFuture 具有良好的异常处理机制，通过 exceptionally() 方法，你可以指定当任务发生异常时应该采取的处理方式。
     *     超时处理： 通过 completeOnTimeout() 方法，CompletableFuture 支持在任务执行超时时提供一个默认的结果。
     *     回调机制： CompletableFuture 提供了类似于回调的机制，可以通过 thenApply(), thenAccept(), thenRun() 等方法来定义在任务完成后执行的操作。
     *     异步编程： CompletableFuture 是 Java 异步编程的一种方式，它提供了更直观、灵活和强大的工具，使得异步编程更加容易理解和实现。
     */
    private void executeVirtualThread2() throws ExecutionException, InterruptedException {
        LocalDateTime start2 = LocalDateTime.now();
        for (int i = 0; i < 1; i++) {
            // runAsync异步执行不带返回值
            CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
                // 虚拟线程的执行逻辑
                try {
                    Thread.sleep(1000);
                    System.out.println("A--------runAsync执行完毕");
                    // 抛出一个异常，给exceptionally捕获执行
                    throw new RuntimeException("runAsync手动抛出异常");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }).exceptionally((t) -> {
                //  该方法用于处理异常情况，返回一个新的 CompletableFuture，当原始 CompletableFuture 异常时，可以提供一个替代的结果。
                System.out.println("A--------异常捕获，打印异常信息" + t.getMessage());
                return null;
            });


            // 异步执行的任务，有返回值
            CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
                // 虚拟线程的执行逻辑
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return "B--------我是supplyAsync的执行结果";
            }).thenApply((thenApplyResult) -> {
                // 该方法在当前 CompletableFuture 完成时触发，根据前一个阶段的结果返回一个新的 CompletableFuture。所以不能对runAsync使用
                System.out.println("B--------supplyAsync执行完成，替换结果：" + thenApplyResult);
                return "B--------supplyAsync执行完成, thenApply执行完成";
            });

            System.out.println(stringCompletableFuture.get());

            CompletableFuture<Void> voidCompletableFuture1 = stringCompletableFuture.thenAccept((s) -> {
                //该方法在当前 CompletableFuture 完成时触发，消耗前一个阶段的结果，但不返回任何内容。如果该方法结尾，那么接受结果必须是CompletableFuture<Void>
                System.out.println("C--------"+s + " 重置为空");
            });

            CompletableFuture<Integer> thenComposeCompletableFuture = CompletableFuture.supplyAsync(() -> "Hello World")
                    .thenCompose((s -> {
//                        // 该方法用于将两个 CompletableFuture 进行组合，将前一个阶段的结果作为后一个阶段的输入。后一个阶段可以对前一个阶段的结果进行篡改，并得到后一个阶段的结果类型
                        return CompletableFuture.supplyAsync(() -> {return s.length();});
                    }));
            System.out.println("D--------"+thenComposeCompletableFuture.get());

            // 需要注意thenCompose 和 thenComposeAsync的使用，是因为带xxxAsync的方法是异步执行的，不会阻塞主线程的逻辑，但是需要使用.join()而不是.get()方法来获取值，因为.join是会等xxxAsync()的异步方法执行完毕再获取结果
            CompletableFuture<Integer> thenComposeAsyncCompletableFuture = CompletableFuture.supplyAsync(() -> "Hello World")
                    .thenComposeAsync((s -> {
//                        // 该方法用于将两个 CompletableFuture 进行组合，将前一个阶段的结果作为后一个阶段的输入。后一个阶段可以对前一个阶段的结果进行篡改，并得到后一个阶段的结果类型
                        return CompletableFuture.supplyAsync(() -> {return s.length();});
                    }));
            System.out.println("D--------"+thenComposeCompletableFuture.join());


            CompletableFuture<Object> exceptionallyCompletableFuture = CompletableFuture.supplyAsync(() -> {throw new RuntimeException("抛出异常");})
                    .exceptionally((ex ) -> {
                        //  该方法用于处理异常情况，返回一个新的 CompletableFuture，当原始 CompletableFuture 异常时，可以提供一个替代的结果。
                        return "E--------异常捕获，返回替代异常：" + ex.getMessage();
                    });
            System.out.println(exceptionallyCompletableFuture.get());




            CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "One");
            CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Two");

            //allOf() 方法接收一个 CompletableFuture 数组（或可变参数列表），并返回一个新的 CompletableFuture。这个新的 CompletableFuture 在传入的所有 CompletableFuture 完成时触发。
            CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(future1, future2);
            CompletableFuture<String> stringCompletableFuture1 = allOfFuture.supplyAsync(() -> {
                return "allOfFuture全部虚拟线程执行完毕";
            });
            System.out.println(stringCompletableFuture1.get());


            //anyOf() 方法接收一个 CompletableFuture 数组（或可变参数列表），并返回一个新的 CompletableFuture。这个新的 CompletableFuture 在传入的任何一个 CompletableFuture 完成时触发。
            CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2);
            CompletableFuture<String> stringCompletableFuture2 = anyOfFuture.supplyAsync(() -> {
                return "anyOfFuture某一个虚拟线程执行完毕";
            });
            System.out.println(stringCompletableFuture2.get());

        }
        LocalDateTime end2 = LocalDateTime.now();
        Duration between2 = Duration.between(start2, end2);
        System.out.println("CompletableFuture耗时2"+between2);
    }

    /**
     * Thread.ofVirtual().start(()->{})
     * 优点：
     * 直观且简单，类似于传统线程的创建方式。
     * 适用于简单的线程创建场景。
     * 缺点：
     * 缺乏一些高级功能，不够灵活。
     * 不能方便地指定线程的一些属性和行为。
     * 适用场景：单独为了异步处理，线程之间互不统属，无返回值
     */
    private void executeVirtualThread1() {
        // 创建一个可继承的线程本地变量
        ThreadLocal<Map<String, Object>> inheritableThreadLocal = new InheritableThreadLocal<>();

        // 设置可继承的线程本地变量的值
        Map<String, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("InheritedValue", "1111111111");
        inheritableThreadLocal.set(objectObjectHashMap);
        LocalDateTime start = LocalDateTime.now();
        for (int i = 0; i < 100; i++) {
            int finalI = i;
            Thread vThread = Thread
                    .ofVirtual()//用于创建虚拟线程的静态工厂方法。虚拟线程是一种轻量级的、用户模式线程，旨在高效处理大量并发任务。
                    .inheritInheritableThreadLocals(false)//用于配置虚拟线程，使其从创建它的线程继承可继承的线程本地变量，默认是true
                    .uncaughtExceptionHandler((Thread t, Throwable e) -> {
                        //处理在线程执行期间抛出的未捕获异常。通过设置虚拟线程的异常处理程序，你可以定义自定义的异常处理逻辑，以便在虚拟线程中发生未捕获异常时进行特定的处理
                        System.out.println("我是uncaughtExceptionHandler：" + e.getMessage());
                    })
                    .name("ofVirtual" + finalI)
                    .start(() -> {
                        // 虚拟线程的执行逻辑

                        //验证inheritInheritableThreadLocals(false)的作用
                        Map<String, Object> map = inheritableThreadLocal.get();
                        if (map == null){
                            System.out.println("inheritInheritableThreadLocals = false");
                        }else{
                            System.out.println("inheritInheritableThreadLocals = true，InheritedValue = " + map.get("InheritedValue"));
                        }

                        // 验证uncaughtExceptionHandler((Thread t, Throwable e) -> {})
                        if (finalI == 1){
                            throw new RuntimeException("我是第一个异常");
                        }
                        try {

                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        if (finalI == 99){
                            System.out.println("我是虚拟线程：" + Thread.currentThread().getName());
                        }
                    });
        }

        LocalDateTime end = LocalDateTime.now();
        Duration between = Duration.between(start, end);
        System.out.println("Thread.ofVirtual耗时"+between);
    }

    /**
     * Scoped Values（作用域值）：允许开发人员在线程之间共享不可变数据。
     * 优点：
     * 缺点：
     * 适用场景：
     */
    @Test
    void scopedValuesTest() throws Exception {

        // 虚拟线程执行静态方法getData
        Thread thread1 = Thread.ofVirtual().start(() -> {
            ScopedValue.runWhere(ScopedValueDTO.scopedValue, "我是作用域值A", ScopedValueService::getData);
        });

        // 虚拟线程执行非静态方法
        ScopedValueService scopedValueService = new ScopedValueService();
        Thread thread2 = Thread.ofVirtual().start(() -> {
            ScopedValue.runWhere(ScopedValueDTO.scopedValue, "我是作用域值B", () -> scopedValueService.getDataB());
            ScopedValue.runWhere(ScopedValueDTO.scopedValue, "我是作用域值C", () -> scopedValueService.getDataB());
        });


        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}
